﻿using System.Diagnostics;
using System.Text;
using TPXSDK.NET.Share;

namespace TPXSDK.NET.Log;

/// <summary>
/// 紧凑型日志解析器，解析BIN单文件
/// </summary>
public class LogParserCompact : LogParserBase
{
    #region 成员变量

    #endregion

    #region 接口实现
    public override void DoLoad(string fileName)
    {
        FilePath = fileName;
        Decode(fileName);
    }

    /// <summary>
    /// 对BIN日志进行解码处理
    /// </summary>
    /// <param name="fn">日志文件名称</param>
    /// <returns>解码是否成功</returns>
    public bool Decode(string fn)
    {
        if (!fn.EndsWith(".bin", StringComparison.InvariantCultureIgnoreCase))
        {// 无效后缀名
            return false;
        }

        // 创建导出文件夹，如果存在使用之
        var dirNmPrev = Path.GetDirectoryName(fn)?.Trim();
        var fnNoExt = Path.GetFileNameWithoutExtension(fn).Trim();
        if (string.IsNullOrEmpty(dirNmPrev)) return false;
        if (string.IsNullOrEmpty(fnNoExt)) return false;

        var dirNm = Path.Combine(dirNmPrev, fnNoExt);
        try
        {
            if (!Directory.Exists(dirNm))
            {
                Directory.CreateDirectory(dirNm);
            }
        }
        catch
        {
            return false;
        }


        const string splitStr = @">>>>>>";
        // 解析文件格式表头
        var sr = new StreamReader(fn);
        var strs = new List<string>();
        while (!sr.EndOfStream)
        {
            var l = sr.ReadLine();
            if (!string.IsNullOrEmpty(l))
            {
                if (l.Contains(splitStr)) break;
                strs.Add(l);
            }
        }
        var writeFs = new List<FileStream>();
        var logEntrys = new List<LogEntryComm>();
        for (int i = 0; i < strs.Count / 3; i++)
        {
            var l1 = strs[i * 3 + 0];
            var l2 = strs[i * 3 + 1];
            var l3 = strs[i * 3 + 2];
            if (string.IsNullOrEmpty(l1) ||
                string.IsNullOrEmpty(l2) ||
                string.IsNullOrEmpty(l3))
            {
                continue;
            }

            var entry = new LogEntryComm
            {
                Name = l1,
                Member = l2.Split(' '),
                Format = Encoding.ASCII.GetBytes(l3)
            };
            logEntrys.Add(entry);
            Trace.WriteLine($"Find Log :{entry.Name}");
            if (!FileUtils.IsValid(entry.Name))
            {
                return false;
            }
            var entryFs = new FileStream(Path.Combine(dirNm, l1), FileMode.Create, FileAccess.Write);
            var cr = new byte[] { 0x0D, 0xA };

            var wrData = Encoding.ASCII.GetBytes(l1);
            entryFs.Write(wrData, 0, wrData.Length); entryFs.Write(cr, 0, cr.Length);
            wrData = Encoding.ASCII.GetBytes(l2);
            entryFs.Write(wrData, 0, wrData.Length); entryFs.Write(cr, 0, cr.Length);
            wrData = Encoding.ASCII.GetBytes(l3);
            entryFs.Write(wrData, 0, wrData.Length); entryFs.Write(cr, 0, cr.Length);
            writeFs.Add(entryFs);
        }

        // 解析参数文件
        var parameter = new StringBuilder();
        while (!sr.EndOfStream)
        {
            var l = sr.ReadLine();
            if (l != null && l.Contains(splitStr)) break;
            parameter.AppendLine(l);
        }

        // 保存到参数文件中
        if (parameter.Length > 0)
        {
            var paraFn = Path.Combine(dirNm, "PARAM.LOG");
            var paraFs = new StreamWriter(paraFn);
            paraFs.Write(parameter.ToString());
            paraFs.Close();
        }

        // 解析二进制数据
        var peekBackCnt = 1024;
        var offset = sr.BaseStream.Position - peekBackCnt;
        sr.Close();
        // 由于StreamReader采用Buffer读机制，无法精确定位Fille Position，需要回查定位
        var fs = new FileStream(fn, FileMode.Open, FileAccess.Read);
        var fileInfo = new FileInfo(fn);

        fs.Seek(offset, SeekOrigin.Begin);
        var br = new BinaryReader(fs);
        var stateCode = 0;
        for (var i = 0; i < peekBackCnt; i++)
        {
            var c = br.ReadByte();
            if (stateCode == 0 && c == 0x0D) stateCode = 1;
            if (stateCode == 1 && c == 0x0A) stateCode = 2;
            if (stateCode == 2 && c == 0x5A) stateCode = 3;
            if (stateCode == 3 && c == 0xA5) stateCode = 4;
            if (stateCode == 4) break;
        }

        if (stateCode != 4)
        {
            foreach (var item in writeFs) item.Close();
            Trace.TraceError("无效文件索引");
            return false;
        }
        fs.Position -= 2;// 归还0x5A A5
        var dataCache = new List<byte>();
        var eventFs = new FileStream(Path.Combine(dirNm, "EVENT.log"), FileMode.Create, FileAccess.Write);
        while (fs.Position < fs.Length)
        {
            var c = br.ReadByte();
            // 找到头部帧头1
            if (dataCache.Count < 2)
            {
                dataCache.Add(c);
                continue;
            }

            if (dataCache[1] != 0xA5 || dataCache[0] != 0x5A)
            {
                dataCache.Clear();// 等待下一个帧同步
                dataCache.Add(c);
                continue;
            }

            dataCache.Add(c);
            var logId = dataCache[2];// 找到LogEntry索引编码
            if (0xEE == logId)
            {
                // 发现Event数据
                var lenLo = br.ReadByte();
                var lenHi = br.ReadByte();
                var dateLen = lenLo + 256 * lenHi;
                var data = br.ReadBytes(dateLen);
                eventFs.Write(data, 0, data.Length);
                dataCache.Clear();// 等待下一个帧同步
                Report((int)(100f * fs.Position / fileInfo.Length));
            }
            else if (logId >= logEntrys.Count)
            {
                // 无效索引
                Trace.TraceError($"Invalid LogIndex:{logId}");
                dataCache.Clear();// 等待下一个帧同步
            }
            else
            {
                var entry = logEntrys[logId];
                var entryFs = writeFs[logId];
                var data = br.ReadBytes(entry.Length);
                entryFs.Write(data, 0, data.Length);
                dataCache.Clear();// 等待下一个帧同步
                Report((int)(100f * fs.Position / fileInfo.Length));
            }
        }

        eventFs.Close();
        br.Close();
        fs.Close();
        foreach (var item in writeFs) item.Close();
        WorkDone();// 通知转换完成
        return true;
    }
    #endregion
}
